package statistics

import (
	"code.google.com/p/gochess/movement"
	"fmt"
	humanize "github.com/dustin/go-humanize"
	"os"
	"reflect"
	"text/tabwriter"
	"time"
)

type Value int64

func (this Value) String() string {
	return humanize.Comma(int64(this))
}

func CalcNPS(nodes Value) Value {
	return Value(float64(nodes) / Duration().Seconds())
}

//func calcNPS(nodes Value) string {
//	return fmt.Sprintf("%s knps", Value(float64(nodes)/Duration().Seconds()/1000))
//}

var (
	// search/search.go
	SearchNodes, LeafNodes, InnerNodes,
	TTHits, GoodTTHits, SearchInCheck,
	RejectedMoves, KillerSaves Value
	Nodes, CutOffs, AlphaRaises [7]Value
	Killer1Available, Killer2Available, Killer1PseudoLegal, Killer2PseudoLegal,
	NodesQuiescence, QuiesceInCheck Value

	// search/transposition_table.go
	HashGetRequests,
	WrittenHashes, OverwrittenHashes,
	OverwrittenSameHashes, AllBucketsFull Value

	FilledBuckets, RestoredBuckets []Value

	// movement/move_list.go
	MoveListSizeForSort [256]Value

	// evaluation/evaluation.go
	PawnHashHits Value

	timer    time.Time
	duration time.Duration
)

func StartTimer() {
	timer = time.Now()
	duration = 0
}

func Duration() time.Duration {
	d := duration
	if d == 0 {
		// search not finnished
		d = time.Now().Sub(timer)
	}
	return d
}

func StopTimer() {
	duration = time.Now().Sub(timer)
}

func ClearStatistics() {

	SearchNodes, LeafNodes, InnerNodes = 0, 0, 0
	TTHits, GoodTTHits = 0, 0
	SearchInCheck, RejectedMoves = 0, 0
	KillerSaves = 0

	for i := range Nodes {
		Nodes[i] = 0
		CutOffs[i] = 0
		AlphaRaises[i] = 0
	}

	Killer1Available, Killer2Available, Killer1PseudoLegal, Killer2PseudoLegal = 0, 0, 0, 0
	NodesQuiescence, QuiesceInCheck = 0, 0

	HashGetRequests, WrittenHashes, OverwrittenHashes,
		OverwrittenSameHashes, AllBucketsFull = 0, 0, 0, 0, 0

	for i := range FilledBuckets {
		FilledBuckets[i] = 0
	}
	for i := range RestoredBuckets {
		RestoredBuckets[i] = 0
	}

	for i := range MoveListSizeForSort {
		MoveListSizeForSort[i] = 0
	}

	PawnHashHits = 0

}

func PrintStatistics() {

	fmt.Printf("Duration: %s\n", duration)
	fmt.Println()
	fmt.Println("Search")
	allNodes := SearchNodes + NodesQuiescence
	fmt.Printf("searched: %v (%v knps)\n", allNodes, CalcNPS(allNodes)/1000)
	fmt.Println()
	fmt.Println("Negamax")
	fmt.Printf("searched: %v; leafs: %v; inner nodes: %v\n",
		SearchNodes, LeafNodes, InnerNodes)
	fmt.Printf("Player in check: %v\n", SearchInCheck)
	fmt.Printf("Rejected moves: %v\n", RejectedMoves)
	fmt.Printf("Killer 1 available: %v, Killer 1 pseudo legal: %v\n",
		Killer1Available, Killer1PseudoLegal)
	fmt.Printf("Killer 2 available: %v, Killer 2 pseudo legal: %v\n",
		Killer2Available, Killer2PseudoLegal)
	var cutOffs Value
	for i := range CutOffs {
		cutOffs += CutOffs[i]
	}
	fmt.Printf("Killer Saves: %v, Cut offs: %v\n", KillerSaves, cutOffs)

	fmt.Println("Nodes per stage:")
	for i := range Nodes {
		fmt.Printf("%v: %v; ", i, Nodes[i])
	}
	fmt.Println()
	fmt.Println("Cut offs per stage:")
	for i := range CutOffs {
		fmt.Printf("%v: %v; ", i, CutOffs[i])
	}
	fmt.Println()
	fmt.Println("Alpha raises per stage:")
	for i := range AlphaRaises {
		fmt.Printf("%v: %v; ", i, AlphaRaises[i])
	}
	//fmt.Println()
	//fmt.Println("Move list size for sort:\n")
	//columnate(MoveListSizeForSort[:], 10)
	fmt.Println()
	fmt.Println("Transposition Table")
	fmt.Printf("Written hashes: %v, Overwritten hashes: %v, Overwritten same hashes: %v\n",
		WrittenHashes, OverwrittenHashes, OverwrittenSameHashes)
	fmt.Printf("All TT buckets full: %v times\n", AllBucketsFull)
	fmt.Print("Filled TT Buckets :")
	for i := range FilledBuckets {
		fmt.Printf("%v: %v; ", i, FilledBuckets[i])
	}
	fmt.Println()
	fmt.Print("Restored TT Buckets: ")
	for i := range RestoredBuckets {
		fmt.Printf("%v: %v; ", i, RestoredBuckets[i])
	}
	fmt.Println()
	fmt.Printf("TranspositionTable.Get(): %v\n", HashGetRequests)
	fmt.Printf("Hash Hits: %v (%d %%), Good Hits: %v (%d %%)\n",
		TTHits, TTHits*100/SearchNodes, GoodTTHits, GoodTTHits*100/SearchNodes)
	if Nodes[0] != 0 {
		fmt.Printf("Hash moves played: %v, Cut offs from hash moves: %v (%d %%)\n",
			Nodes[0], CutOffs[0], CutOffs[0]*100/Nodes[0])
	} else {
		fmt.Printf("No Hash moves played.\n")
	}
	fmt.Println()
	fmt.Println("Quiescence search")
	fmt.Printf("searched: %v\n", NodesQuiescence)
	fmt.Printf("stm in check: %v\n", QuiesceInCheck)
	Print(&movement.Stats)
	/*		fmt.Printf("Pawn Hash Hits: %v (%v %%)\n",
			eval.PawnHashHits, eval.PawnHashHits*100/LeafNodes)	*/
}

func columnate(values []int64, cols int) {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 0, 2, ' ', tabwriter.AlignRight)
	max := 0
	for i := range values {
		if values[i] != 0 {
			max = i
		}
	}
	// fill up row
	max = max/cols*cols + cols - 1
	for i := 0; i <= max; i++ {
		if (i)%cols == 0 {
			fmt.Fprintf(w, "%d\t", i)
		}
		if values[i] != 0 {
			fmt.Fprintf(w, "%d\t", values[i])
		}
		if (i+1)%cols == 0 {
			fmt.Fprintln(w)
		}
	}
	w.Flush()
}

func Print(stats interface{}) {
	w := new(tabwriter.Writer)
	w.Init(os.Stdout, 0, 0, 2, ' ', 0)
	v := reflect.ValueOf(stats).Elem()
	t := v.Type()
	for i := 0; i < t.NumField(); i++ {
		fmt.Fprintf(w, "%v:\t%15v\n", t.Field(i).Name, Value(v.Field(i).Int()))
	}
	w.Flush()
}
